package pattern_test;

import java.util.ArrayList;

import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;

public class BeatModel implements BeatModelInterface, MetaEventListener
{
	
	Sequencer sequencer;
	ArrayList beatObservers = new ArrayList();
	ArrayList bpmObservers = new ArrayList();
	Sequence sequence;
	Track track;
	
	int bpm = 90;
	
	public void BeatModel()
	{
	}
	
	
	public void initialize()
	{
		setUpMidi();
		buildTrackAndStart();
	}
	
	public void on()
	{
		sequencer.start();
		setBPM(90);		
	}
	
	public void off()
	{
		setBPM(0);
		sequencer.stop();
	}
	
	public void setBPM(int bpm)
	{
		this.bpm = bpm;
		sequencer.setTempoInBPM(bpm);
		notifyBPMObservers();		
	}

	public int getBPM()
	{
		return bpm;
	}
	
	void beatEvent()
	{
		notifyBeatObservers();
	}

	@Override
	public void registerObserver(BeatObserver o) {
		// TODO Auto-generated method stub
		beatObservers.add(o);
		
	}

	@Override
	public void removeObserver(BeatObserver o) {
		// TODO Auto-generated method stub
		int i = beatObservers.indexOf(o);
		if (i >= 0) {
			beatObservers.remove(i);
		}
		
	}
	
	private void notifyBeatObservers() {
		// TODO Auto-generated method stub
		for(int i = 0; i < beatObservers.size(); i++) {
			BeatObserver observer = (BeatObserver)beatObservers.get(i);
			observer.updateBeat();
		}
	}

	@Override
	public void registerObserver(BPMObserver o) {
		// TODO Auto-generated method stub
		bpmObservers.add(o);
		
	}

	@Override
	public void removeObserver(BPMObserver o) {
		// TODO Auto-generated method stub
		int i = bpmObservers.indexOf(o);
		if (i >= 0) {
			beatObservers.remove(i);
		}
		
	}
	
	private void notifyBPMObservers() {
		// TODO Auto-generated method stub
		for(int i = 0; i < bpmObservers.size(); i++) {
			BPMObserver observer = (BPMObserver)bpmObservers.get(i);
			observer.updateBPM();
		}
		
	}

	
	private void buildTrackAndStart() {
		// TODO Auto-generated method stub
		 int[] trackList = {35, 0, 46, 0};
		    
	     sequence.deleteTrack(null);
	     track = sequence.createTrack();

	     makeTracks(trackList);
	     track.add(makeEvent(192,9,1,0,4));      
		 try {
			sequencer.setSequence(sequence);                    
		 	} catch(Exception e) {
			e.printStackTrace();
		}
		
	}

	private MidiEvent makeEvent(int i, int j, int k, int l, int m) {
		// TODO Auto-generated method stub
		 MidiEvent event = null;
	        try {
	            ShortMessage a = new ShortMessage();
	            a.setMessage(i, j, k, l);
	            event = new MidiEvent(a, m);
	            
	        } catch(Exception e) {
				e.printStackTrace(); 
			}
	        return event;
	}

	private void makeTracks(int[] trackList) {
		// TODO Auto-generated method stub
		for (int i = 0; i < trackList.length; i++) {
	          int key = trackList[i];

	          if (key != 0) {
	             track.add(makeEvent(144,9,key, 100, i));
	             track.add(makeEvent(128,9,key, 100, i+1));
	          }
	       }
		
	}

	private void setUpMidi() {
		// TODO Auto-generated method stub
		try {
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.addMetaEventListener(this);
			sequence = new Sequence(Sequence.PPQ,4);
			track = sequence.createTrack();
			sequencer.setTempoInBPM(getBPM());
		} catch(Exception e) {
				e.printStackTrace();
		}
	}

	@Override
	public void meta(MetaMessage arg0) {
		// TODO Auto-generated method stub
		if (arg0.getType() == 47) {
			beatEvent();
        	sequencer.start();
        	setBPM(getBPM());
        }
		
	}
}